home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 February
/
EnigmA AMIGA RUN 04 (1996)(G.R. Edizioni)(IT)[!][issue 1996-02][Skylink CD III].iso
/
earcd
/
util1
/
shell-10.lha
/
shell-1.0
/
src
/
if.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-07
|
20KB
|
930 lines
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dos/rdargs.h>
#include <dos/stdio.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include "util.h"
#define NODEBUG 1
#ifdef DEBUG
# define Dputs puts
#else
# define Dputs(x)
#endif
/*
Now the template to collect all arguments of IF/ELIF/ELSEIF. They
are the same as for Amiga IF, with these exceptions:
ARGS/M - here I collect everything which is not an argument
to an option or a flag. eg. the line
IF str1 EQ str2
will yield "str2" as the argument of EQ and "str1"
will be the first thing in ARGS.
[/S - This is a trick. By making [ a flag, I can ask for it
and need not check ARGS.
*/
const char arg_template[] = "ARGS/M,NOT/S,WARN/S,ERROR/S,FAIL/S,EQ/K,GT/K,\
GE/K,VAL/S,EXISTS/K,[/S";
/*
This is the structure for the arguments to IF. I don't use
an LONG[]-Array since this way, it's much easier and error-prone
to lookup an argument (and I need only ONE cast when I call ReadArgs()
and not every time when I access the argument.
*/
struct IfArgs
{
char ** args;
ULONG not;
ULONG warn;
ULONG error;
ULONG fail;
char * eq;
char * gt;
char * ge;
ULONG val;
char * exists;
ULONG test;
};
/*
This is the second template. It is used if the condition of IF doesn't
hold. Then I continue reading input (thus effectively read the next
line of input). The input is split up into garbage (REST) and a
couple of flags: IF, ELSE, ENDIF, FI, ELIF, ELSEIF. If I find IF,
I'll note that the next ELSE/ENDIF is meaningless and whenever
I find an ENDIF/FI, I check if it's the matching one for my IF.
See below how to continue reading input. Note that I still need
more commands (ENDIF, ELSE, etc.) since there can be occasions when the
shell sees them (eg. when I skip the THEN body to look up the optional
ELSE. When the ELSE branch is executed, the shell will eventually
hit the ENDIF).
*/
const char cont_template[] = "REST/M,IF/S,ELSE/S,ENDIF/S,FI/S,ELIF/S,ELSEIF/S";
/* This structure is used to filter the body of the IF. */
struct ContStruct
{
char ** rest;
ULONG is_if;
ULONG is_else;
ULONG is_endif;
ULONG is_fi;
ULONG is_elif;
ULONG is_elseif;
};
/* Prototypes */
int test_amiga (struct IfArgs *);
int test_unix (struct IfArgs *);
int exists (char *);
/*
Main function. argc and argv are ignored since ReadArgs() cooks the
parameters for me.
*/
int main (int argc, char ** argv)
{
struct IfArgs arg_struct; /* The calling parameters */
struct ContStruct cont_struct; /* The contents of a line of the
stuff following the IF */
struct RDArgs * rda, /* Reader for the Arguments to IF */
* cont_rda; /* Reader for the stuff following
the IF (see below) */
int level; /* Nesting level for IFs */
BPTR old_in, /* Original value of Input() */
current_input; /* *magic* (See below) */
int cond; /* The result of the evaluation of
the IF condition. */
/* Clear my args */
memset (&arg_struct, 0, sizeof(arg_struct));
/* Analyze args */
if ( (rda = ReadArgs ((char *)arg_template, (LONG *)&arg_struct, NULL)) )
{
/* If I have any Amiga-Keywords, I evaluate them Amiga-like */
if (arg_struct.not || arg_struct.warn || arg_struct.error ||
arg_struct.fail || arg_struct.val || arg_struct.eq ||
arg_struct.gt || arg_struct.ge || arg_struct.exists)
{
cond = test_amiga (&arg_struct);
}
else /* Otherwise, we try the UN*X bourne shell version */
{
cond = test_unix (&arg_struct);
}
#ifdef DEBUG
printf ("IF = %s\n", cond ? "TRUE" : "FALSE");
#endif
/*
Here is the magic part. If the condition is FALSE, we must
continue execution after the next ELSE or ENDIF (FI, ELIF,
etc.). How do we do that ? In fact, it's pretty simple: You can
continue reading input ! That input doesn't come from stdin,
though, but from cli->cli_CurrentInput. This stream points into
the script OR interactive shell where the line came from that
called us. So I switch stdin to cli_CurrentInput, read until
the next entry point is found and then I exit - the stream
handle will then point to the next valid command and the shell
will happily continue execution. The same happens when an ELSE
is found. ELSE is just more simple since no condition must be
evaluated.
*/
if (!cond)
{
LONG current_pos;
int c;
level = 0; /* No nesting if IF/ELSE/ENDIF's yet */
/*
now I fetch the BPTRs to stdin and CurrentInput to swap
them. Later, when the input has been processed, I'll
undo that.
*/
current_input = Cli()->cli_CurrentInput;
old_in = Input ();
/* Set new input stream */
SelectInput (current_input);
/*
This is heavy magic. ReadArgs() needs and buffered
stream and the FGetC()/Flush()-Pair provides for that.
The UnGetC() puts the character back we read.
*/
c = FGetC (current_input);
Flush (current_input);
UnGetC (current_input, c);
for (;;)
{
/*
Remember the current position in case we find an
ELSEIF. When we hit an ELSEIF, we seek back to the
beginning of the line and exit. Then the shell will see
the ELSEIF as the next command and continue with it.
*/
current_pos = Seek (current_input, 0, OFFSET_CURRENT);
/* Clear the argument structure */
memset (&cont_struct, 0, sizeof(cont_struct));
/* Read a line of input */
if (!(cont_rda = ReadArgs ((char *)cont_template,
(LONG *)&cont_struct, NULL)) )
{
PrintFault (IoErr(), "ContReadArgs");
cond = 10;
break;
}
/* now look what we got. */
if (cont_struct.is_if)
{
/* If it's an IF..., we increase the nesting level. */
level ++;
}
else if (cont_struct.is_endif || cont_struct.is_fi ||
cont_struct.is_else)
{
/*
The IF has ended (either bei ENDIF or ELSE).
We can handle ELSE here, too since the condition
was FALSE and thus we have to execute the
commands in the ELSE branch. Also we stop only
if level is 0 to allow nesting.
*/
if (!level)
break;
level --;
}
else if (cont_struct.is_elif || cont_struct.is_elseif)
{
/*
ELSEIF found. Seek back and continue with the
beginning of the line.
*/
if (!level)
{
if (Seek (current_input, current_pos,
OFFSET_BEGINNING) == -1)
{
PrintFault (IoErr(), "Seek back");
cond = 10;
}
break;
}
}
/*
If nothing interesting was found, we just ignore it
(that are the lines which make up the THEN branch.
*/
/* Free memory and read the next line */
FreeArgs (cont_rda);
} /* for */
/* No more input but nesting level != 0 ? Something's wrong. */
if (level)
{
fprintf (stderr, "Missing ELSE or ENDIF");
cond = 10;
}
/* Undo magic */
SelectInput (old_in);
/*
If we had to abort the loop, we might have to free
memory here.
*/
if (cont_rda)
FreeArgs (cont_rda);
}
/* Free original arguments */
FreeArgs (rda);
}
else
{
PrintFault (IoErr(), "ReadArgs() Error");
cond = 10;
}
/* Return error code (1 as "IF TRUE" must be filtered out) */
return (cond < 5) ? 0 : cond;
} /* main */
/* Check if a file exists */
int exists (char * fname)
{
BPTR lock;
int cond;
lock = Lock (fname, SHARED_LOCK);
if ( (cond = (lock != NULL)) )
UnLock (lock);
return cond;
} /* exists */
/* Compare the dates of two files. The result will be
-1 if fn1 is older than fn2
0 if fn1 and fn2 have the same date
1 if fn1 is newer than fn2
10 if an error occurred. In this case, a message has been
printed.
*/
int compare_dates (char * fn1, char * fn2)
{
int cond = 10;
__aligned struct FileInfoBlock fib1, fib2;
BPTR lock1, lock2;
/* 1. Lock both files */
lock1 = Lock (fn1, SHARED_LOCK);
if (lock1)
{
lock2 = Lock (fn2, SHARED_LOCK);
if (!lock2)
PrintFault (IoErr(), fn2);
}
else
PrintFault (IoErr(), fn1);
/* 2. If that worked, Examine() the files */
if (lock1 && lock2)
{
if (!Examine (lock1, &fib1))
{
PrintFault (IoErr(), fn1);
}
else if (!Examine (lock2, &fib2))
{
PrintFault (IoErr(), fn2);
}
else
{
/*
Compare the dates and truncate the result (CompareDates()
can return -15 and the like)
*/
cond = CompareDates (&fib2.fib_Date, &fib1.fib_Date);
if (cond < 0)
cond = -1;
else if (cond > 0)
cond = 1;
}
}
/* 3. Release the files */
if (lock1)
{
UnLock (lock1);
if (lock2)
UnLock (lock2);
}
/* Return the result */
return cond;
} /* compare_dates */
/* Evaluate a standard Amiga IF */
int test_amiga (struct IfArgs * args)
{
int cond;
/* Case 1: Check Result */
if (args->warn || args->error || args->fail)
{
char buffer[32];
int level;
/* Get the result of the last command */
GetVar ("RC", buffer, sizeof (buffer), GVF_LOCAL_ONLY);
/* Choose a level */
if (args->warn)
level = 5;
else if (args->error)
level = 10;
else
level = 20;
/* Compare result and the level */
cond = atoi(buffer) >= level;
}
else if (args->exists) /* IF EXISTS */
{
cond = exists (args->exists);
}
else
{
if (args->val) /* Numeric compare: val1 ?? val2 */
{
int val1, val2;
if (!args->args)
{
fprintf (stderr, "Missing second argument");
return 10;
}
/* Note that the FIRST figure is in args[] ! */
val1 = atoi (args->args[0]);
if (args->eq)
{
val2 = atoi (args->eq);
cond = val1 == val2;
}
else if (args->gt)
{
val2 = atoi (args->gt);
cond = val1 > val2;
}
else if (args->ge)
{
val2 = atoi (args->ge);
cond = val1 >= val2;
}
else
{
fprintf (stderr, "Missing arguments");
return 10;
}
}
else /* String compare: str1 ?? str2*/
{
char * str1, * str2;
if (!args->args)
{
fprintf (stderr, "Missing second argument");
return 10;
}
/* Note that the FIRST string is in args[] ! */
str1 = args->args[0];
if (args->eq)
{
str2 = args->eq;
cond = Stricmp (str1, str2) == 0;
}
else if (args->gt)
{
str2 = args->gt;
cond = Stricmp (str1, str2) > 0;
}
else if (args->ge)
{
str2 = args->ge;
cond = Stricmp (str1, str2) >= 0;
}
else
{
fprintf (stderr, "Missing arguments\n");
return 10;
}
}
}
/* Return result (negated if neccessary */
return args->not ? !cond : cond;
} /* test_amiga */
int test (char *** args)
{
char ** arg = *args;
int cond = -1;
int not = 0;
int found_string = FALSE;
int stop = 0;
while (*arg && cond < 5 && !stop)
{
if ((*arg)[0] == '(' && !(*arg)[1])
{
arg ++;
cond = test (&arg);
if (cond < 0)
{
fprintf (stderr, "IF: Missing arguments in ( EXPRESSSION )\n");
cond = 10;
}
}
else if ((*arg)[0] == ')' && !(*arg)[1])
{
arg ++;
break;
}
else if ((*arg)[0] == ']' && !(*arg)[1])
{
if (arg[1] && !((*arg)[0]==';' && !(*arg)[1]))
{
fprintf (stderr, "IF: Unexpected arguments after ]\n");
cond = 10;
}
break;
}
else if ((*arg)[0] == '!')
{
if (!(*arg)[1])
{
not = !not;
arg ++;
}
else if ((*arg)[1] == '=')
{
if (arg[1] && found_string)
{
cond = strcmp (arg[-1], arg[1]) != 0;
arg += 2;
}
else
{
if (found_string)
{
fprintf (stderr, "IF: Missing second argument to !=\n");
cond = 10;
}
else
{
fprintf (stderr, "IF: Missing first argument to !=\n");
cond = 10;
}
}
}
else
{
fprintf (stderr, "IF: Unknown argument %s\n", *arg);
cond = 10;
}
}
else if ((*arg)[0] == '=' && !(*arg)[1])
{
if (arg[1] && found_string)
{
cond = strcmp (arg[-1], arg[1]) == 0;
arg += 2;
}
else
{
if (found_string)
{
fprintf (stderr, "IF: Missing second argument to =\n");
cond = 10;
}
else
{
fprintf (stderr, "IF: Missing first argument to =\n");
cond = 10;
}
}
}
else if ((*arg)[0] == '-')
{
char * ptr;
ptr = *arg+1;
if (!arg[1])
{
fprintf (stderr, "IF: Missing argument to %s\n", *arg);
cond = 10;
}
else
{
switch (*ptr)
{
case 'a': /* AND */
if (cond == -1)
{
fprintf (stderr, "IF: Missing first argument to -a\n");
cond = 10;
}
else
{
if (!cond)
stop = 1;
arg ++;
}
break;
case 'o': /* OR or OLDER_THAN (ot) */
if (!ptr[1]) /* -o */
{
if (cond == -1)
{
fprintf (stderr, "IF: Missing first argument to -o\n");
cond = 10;
}
else
{
if (cond)
stop = 1;
arg ++;
}
}
else
{
/* Older than */
if (!found_string)
{
fprintf (stderr, "IF: Missing first argument to -ot\n");
cond = 10;
}
else
{
cond = compare_dates (arg[-1], arg[1]);
if (cond != 10)
cond = cond < 0;
arg += 2;
}
}
break;
case 'n': /* "String has non-zero length" or "Not Equal" (ne)
or "File1 is newer than File2" (nt) */
ptr ++;
switch (*ptr)
{
case 0:
cond = arg[1][0]; /* -n */
arg += 2;
break;
case 'e': /* -ne */
if (found_string)
{
cond = atoi(arg[-1]) != atoi(arg[1]);
arg += 2;
}
else
{
fprintf (stderr, "IF: Missing first argument to -ne\n");
cond = 10;
}
break;
case 't': /* -nt */
/* Newer than */
if (found_string)
{
cond = compare_dates (arg[-1], arg[1]);
arg += 2;
if (cond != 10)
cond = cond > 0;
}
else
{
fprintf (stderr, "IF: Missing first argument to -nt\n");
cond = 10;
}
break;
}
break;
case 'z': /* "String has zero length" */
cond = !arg[1][0];
arg ++;
break;
case 'e': /* "File exists" or "Files are the same" (ef) or
"Numbers are equal" (eq) */
ptr ++;
switch (*ptr)
{
case 0:
cond = exists (arg[1]);
arg += 2;
break;
case 'f': {
BPTR f1, f2;
if (found_string)
{
if (!(f1 = Lock (arg[-1], SHARED_LOCK)) )
{
PrintFault (IoErr(), arg[-1]);
cond = 10;
}
else
{
if (!(f2 = Lock (arg[1], SHARED_LOCK)) )
{
PrintFault (IoErr(), arg[1]);
cond = 10;
}
else
{
cond = SameLock (f1, f2) == 0;
arg += 2;
UnLock (f2);
}
UnLock (f1);
}
}
else
{
fprintf (stderr, "IF: Missing first argument to -ef\n");
cond = 10;
}
break; }
case 'q':
if (found_string)
{
cond = atoi(arg[-1]) == atoi(arg[1]);
arg += 2;
}
else
{
fprintf (stderr, "IF: Missing first argument to -eq\n");
cond = 10;
}
break;
}
break;
case 'g': /* "I1 >= I2" (ge) or "I1 > I2" (gt) or
"FILE exists and is set-group-ID" */
case 'l': /* "I1 <= I2" (le) or "I1 < I2" (lt) */
{
int i1, i2;
if (!ptr[1])
{
cond = FALSE;
break;
}
if (found_string)
{
i1 = atoi(arg[-1]);
i2 = atoi(arg[1]);
arg += 2;
cond = (*ptr == 'g') ? i1 > i2 : i1 < i2;
if (!cond && ptr[1] == 'e')
cond = i1 == i2;
}
else
{
fprintf (stderr, "IF: Missing first argument to %s\n", *arg);
cond = 10;
}
}
break;
case 't': /* standard output is opened on a terminal */
cond = IsInteractive (Output ()) != 0;
arg ++;
break;
/* The next cases are always false due to AmigaDOS */
case 'S': /* FILE exists and is a socket */
case 'b': /* FILE exists and is block special */
case 'c': /* FILE exists and is character special */
case 'k': /* FILE exists and has its sticky bit set */
case 'u': /* FILE exists and its set-user-ID bit is set */
cond = FALSE;
arg += 2;
break;
/* The next cases are always true due to AmigaDOS */
case 'G': /* File exists and is owned by the effective group ID */
case 'O': /* FILE exists and is owned by the effective user ID */
cond = TRUE;
arg += 2;
break;
case 'L': /* FILE exists and is a symbolic link */
case 'p': /* FILE exists and is a named pipe */
case 'd': /* FILE exists and is a directory */
case 'f': /* FILE exists and is a regular file */
case 's': /* FILE exists and has a size greater than zero */
case 'r': /* FILE exists and is readable */
case 'w': /* FILE exists and is writable */
case 'x': /* FILE exists and is executable */
{
__aligned struct FileInfoBlock fib;
BPTR lock;
lock = Lock (arg[1], SHARED_LOCK);
if (lock)
{
if (Examine (lock, &fib))
{
switch (*ptr)
{
case 'L': /* symbolic link */
cond = fib.fib_DirEntryType == ST_LINKFILE ||
fib.fib_DirEntryType == ST_LINKDIR ||
fib.fib_DirEntryType == ST_SOFTLINK;
break;
case 'p': /* named pipe */
cond = fib.fib_DirEntryType == ST_PIPEFILE;
break;
case 'd': /* directory */
cond = fib.fib_DirEntryType == ST_ROOT ||
fib.fib_DirEntryType == ST_USERDIR;
break;
case 'f': /* regular file */
cond = fib.fib_DirEntryType == ST_FILE;
break;
case 's': /* size greater than zero */
cond = fib.fib_DirEntryType == ST_FILE &&
fib.fib_Size;
break;
case 'r': /* readable */
cond = !(fib.fib_Protection & FIBF_READ);
break;
case 'w': /* writable */
cond = !(fib.fib_Protection & FIBF_WRITE);
break;
case 'x': /* executable */
cond = !(fib.fib_Protection & FIBF_EXECUTE) ||
(fib.fib_Protection & FIBF_SCRIPT);
break;
} /* switch */
}
else
{
fprintf (stderr, "IF TEST ... %s %s:", arg[0],
arg[1]);
PrintFault (IoErr(), NULL);
cond = 10;
}
UnLock (lock);
}
else
cond = FALSE;
arg += 2;
} /* case */
} /* switch */
} /* if */
found_string = FALSE;
}
else if ((*arg)[0])
{
cond = 1;
if (!found_string)
found_string = TRUE;
else
{
fprintf (stderr, "IF TEST: Unexpected argument \"%s\"\n",
*arg);
cond = 10;
}
arg ++;
}
else
{
fprintf (stderr, "IF TEST: Unexpected empty argument\n");
cond = 10;
}
}
/* If we terminated early, skip until a ) or ] */
if (stop)
{
while (*arg
&& !((*arg)[0] == ')' && !(*arg)[1])
&& !((*arg)[0] == ']' && !(*arg)[1])
)
{
arg ++;
}
}
*args = arg;
if (cond == -1)
{
fprintf (stderr, "IF TEST: Missing arguments\n");
cond = 10;
}
if (cond > 5)
return cond;
return not ? !cond : cond;
} /* test */
int test_unix (struct IfArgs * args)
{
char ** arg = args->args;
if (!arg)
{
fprintf (stderr, "Missing arguments for UN*X-like IF\n");
return 10;
}
if (args->test) /* if test is set, implicitly run TEST */
{
return test (&arg);
}
else /* run all arguments as a command and examine the result */
{
int t;
for (t=0; arg[t]; t++)
{
if (arg[t][0] == ';' && !arg[t][1])
break;
}
return execute_command (t, arg) == 0;
}
return 0;
} /* test_unix */